package com.lijinjiang.component;

import com.lijinjiang.constant.Colors;
import com.lijinjiang.constraints.DateSelectionConstraint;
import com.lijinjiang.mode.PickerMode;
import com.lijinjiang.model.DateModel;
import com.lijinjiang.model.LocalDateModel;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * @Description 日历面板
 * @Author lijinjiang
 * @Date 2024-12-30 10:52
 */
public class CalendarPanel extends JPanel {
    private final Set<DateSelectionConstraint> dateConstraints;
    private final DateModel<?> dateModel;
    private final PickerMode pickerMode;
    private final CalendarModel calendarModel;

    public CalendarPanel(DateModel<?> dateModel, PickerMode pickerMode) {
        this.dateModel = dateModel;
        this.pickerMode = pickerMode;
        dateConstraints = new HashSet<>();
        calendarModel = new CalendarModel(dateModel);
        this.setLayout(new BorderLayout());
        this.setOpaque(false);
        this.add(getDayTableHeader(), BorderLayout.NORTH);
        this.add(getDayTable(), BorderLayout.CENTER);
    }

    /**
     * 该模型表示所选日期。该模型实现了显示日期的TableModel接口
     */
    protected static class CalendarModel implements TableModel, SpinnerModel, ChangeListener {

        private final DateModel<?> model;
        private final Set<ChangeListener> spinnerChangeListeners;
        private final Set<TableModelListener> tableModelListeners;

        public CalendarModel(DateModel<?> model) {
            this.spinnerChangeListeners = new HashSet<>();
            this.tableModelListeners = new HashSet<>();

            this.model = model;
            model.addChangeListener(this);
        }

        public DateModel<?> getModel() {
            return model;
        }

        public void addChangeListener(ChangeListener arg0) {
            spinnerChangeListeners.add(arg0);
        }

        public void removeChangeListener(ChangeListener arg0) {
            spinnerChangeListeners.remove(arg0);
        }

        public Object getNextValue() {
            return Integer.toString(model.getYear() + 1);
        }

        public Object getPreviousValue() {
            return Integer.toString(model.getYear() - 1);
        }

        public void setValue(Object text) {
            String year = (String) text;
            model.setYear(Integer.parseInt(year));
        }

        public Object getValue() {
            return Integer.toString(model.getYear());
        }

        public void addTableModelListener(TableModelListener arg0) {
            tableModelListeners.add(arg0);
        }

        public void removeTableModelListener(TableModelListener arg0) {
            tableModelListeners.remove(arg0);
        }

        public int getColumnCount() {
            return 7;
        }

        public int getRowCount() {
            return 6;
        }

        public String getColumnName(int columnIndex) {
            switch (columnIndex) {
                case 0:
                    return "一";
                case 1:
                    return "二";
                case 2:
                    return "三";
                case 3:
                    return "四";
                case 4:
                    return "五";
                case 5:
                    return "六";
                case 6:
                    return "日";
                default:
                    return null;
            }
        }


        /**
         * TableModel的日期
         * 上个月 (... -1, 0) ->
         * 当前月 (1...DAYS_IN_MONTH) ->
         * 下个月 (lengthOfMonth + 1, lengthOfMonth + 2, ...)
         */
        public Object getValueAt(int rowIndex, int columnIndex) {
            LocalDate date = LocalDate.of(model.getYear(), model.getMonth(), 1);//当月的第一天
            DayOfWeek weekday = date.getDayOfWeek();
            int beforeDays = weekday.getValue() - 1;
            int series = columnIndex + rowIndex * 7 + 1;
            return series - beforeDays;
        }

        @SuppressWarnings({"rawtypes"})
        public Class getColumnClass(int arg0) {
            return Integer.class;
        }

        public boolean isCellEditable(int arg0, int arg1) {
            return false;
        }

        public void setValueAt(Object arg0, int arg1, int arg2) {
        }

        //每当对模型值进行更改时调用。通知内部监听器并更新控件
        private void fireValueChanged() {
            for (ChangeListener cl : spinnerChangeListeners) {
                cl.stateChanged(new ChangeEvent(this));
            }

            for (TableModelListener tl : tableModelListeners) {
                tl.tableChanged(new TableModelEvent(this));
            }
        }

        public void stateChanged(ChangeEvent e) {
            fireValueChanged();
        }

    }

    private JTable dayTable;

    private JTableHeader dayTableHeader;

    private CalendarTableCellRenderer dayTableCellRenderer;

    private JTable getDayTable() {
        if (dayTable == null) {
            dayTable = new JTable();
            dayTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
            dayTable.setModel(calendarModel);
            dayTable.setShowGrid(true);
            dayTable.setGridColor(Colors.BG_CALENDAR_GRID);
            dayTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            dayTable.setCellSelectionEnabled(true);
            dayTable.setRowSelectionAllowed(true);
            dayTable.setFocusable(false);
            dayTable.addMouseListener(new MouseAdapter() {
                @Override public void mousePressed(MouseEvent e) {
                    int row = dayTable.getSelectedRow();
                    int col = dayTable.getSelectedColumn();
                    DateModel<?> model = calendarModel.getModel();
                    if (row >= 0 && row <= 5) {
                        int oldDay = model.getDay();
                        LocalDate currentMonth = LocalDate.of(model.getYear(), model.getMonth(), 1);
                        Integer cellValue = (Integer) calendarModel.getValueAt(row, col);
                        if (cellValue < 1) {//上个月
                            LocalDate lastMonth = currentMonth.minusMonths(1);
                            model.setDate(lastMonth.getYear(), lastMonth.getMonthValue(), lastMonth.lengthOfMonth() + cellValue);
                        } else if (cellValue > currentMonth.lengthOfMonth()) {//下个月
                            LocalDate nextMonth = currentMonth.plusMonths(1);
                            model.setDate(nextMonth.getYear(), nextMonth.getMonthValue(), cellValue - currentMonth.lengthOfMonth());
                        } else {
                            model.setDate(currentMonth.getYear(), currentMonth.getMonthValue(), cellValue);
                        }
                        if (!checkValid(model)) {
                            model.setDay(oldDay);//回滚
                        }
                    }
                    if (pickerMode == PickerMode.DAY) {//选择日的模式，点击日期选中
                        model.setSelected(true);
                    }
                    dayTable.repaint();
                }
            });
            for (int i = 0; i < 7; i++) {
                TableColumn column = dayTable.getColumnModel().getColumn(i);
                column.setCellRenderer(getDayTableCellRenderer());
            }
            dayTable.addComponentListener(new ComponentListener() {

                public void componentResized(ComponentEvent e) {
                    //表的新大小
                    final double w = e.getComponent().getSize().getWidth();
                    final double h = e.getComponent().getSize().getHeight();

                    //将字体的大小设置为宽度或高度的一部分，以最小的为准
                    final float sw = (float) Math.floor(w / 16);
                    final float sh = (float) Math.floor(h / 8);
                    dayTable.setFont(dayTable.getFont().deriveFont(Math.min(sw, sh)));

                    //将行高度设置为高度的分数
                    final int r = (int) Math.floor(h / 6);
                    dayTable.setRowHeight(r);
                }

                public void componentMoved(ComponentEvent e) {}

                public void componentShown(ComponentEvent e) {}

                public void componentHidden(ComponentEvent e) {}

            });
        }
        return dayTable;
    }

    private CalendarTableCellRenderer getDayTableCellRenderer() {
        if (dayTableCellRenderer == null) {
            dayTableCellRenderer = new CalendarTableCellRenderer();
        }
        return dayTableCellRenderer;
    }

    private JTableHeader getDayTableHeader() {
        if (dayTableHeader == null) {
            dayTableHeader = getDayTable().getTableHeader();
            dayTableHeader.setResizingAllowed(false);
            dayTableHeader.setReorderingAllowed(false);
            dayTableHeader.setDefaultRenderer(getDayTableCellRenderer());
        }
        return dayTableHeader;
    }

    /**
     * 这个内部类呈现日期表，根据它是否在当月、是否在今天、是否被选中等来设置颜色。
     */
    private class CalendarTableCellRenderer extends DefaultTableCellRenderer {

        private static final long serialVersionUID = 1L;

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            // 如果在遇到值为空，退出此方法
            if (value == null) {
                return super.getTableCellRendererComponent(table, null, isSelected, hasFocus, row, column);
            }

            JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            label.setHorizontalAlignment(JLabel.CENTER);

            if (row == -1) {
                label.setForeground(Colors.FG_CALENDAR_HEADER);
                label.setBackground(Colors.BG_CALENDAR_HEADER);
                label.setHorizontalAlignment(JLabel.CENTER);
                return label;
            }

            LocalDate today = LocalDate.now();//今天
            LocalDate selectedDate = LocalDate.of(dateModel.getYear(), dateModel.getMonth(), dateModel.getDay());//当前天
            int lastDayOfMonth = selectedDate.lengthOfMonth();
            int cellDayValue = (Integer) value;//表格中天数值
            LocalDate cellOfDate;//单元格代表的日期

            if (cellDayValue < 1 || cellDayValue > lastDayOfMonth) {//选中日期非当月
                label.setForeground(Colors.FG_CALENDAR_GRID_OTHER_MONTH);
                if (cellDayValue < 1) {//上个月
                    LocalDate lastMonth = selectedDate.minusMonths(1);//减一个月
                    int lastDayLastMonth = lastMonth.lengthOfMonth();
                    int days = lastDayLastMonth + cellDayValue;
                    label.setText(Integer.toString(days));//设置显示日期
                    cellOfDate = lastMonth.withDayOfMonth(days);//单元格日期
                } else {//下个月
                    LocalDate nextMonth = selectedDate.plusMonths(1);//加一个月
                    int days = cellDayValue - lastDayOfMonth;
                    label.setText(Integer.toString(days));
                    cellOfDate = nextMonth.withDayOfMonth(days);//单元格日期
                }
                DateModel<LocalDate> modelForDay = new LocalDateModel(cellOfDate);
                label.setBackground(checkValid(modelForDay) ? Colors.BG_CALENDAR_GRID : Colors.BG_CALENDAR_GRID_NOT_SELECTABLE);
            } else {//选中日期当月
                label.setForeground(Colors.FG_CALENDAR_GRID_THIS_MONTH);//单元格前景色

                cellOfDate = selectedDate.withDayOfMonth(cellDayValue);//单元格日期

                DateModel<LocalDate> modelForDay = new LocalDateModel(cellOfDate);
                label.setBackground(checkValid(modelForDay) ? Colors.BG_CALENDAR_GRID : Colors.BG_CALENDAR_GRID_NOT_SELECTABLE);

                //今天
                if (today.getDayOfMonth() == cellDayValue && today.getMonthValue() == dateModel.getMonth() && today.getYear() == dateModel.getYear()) {
                    label.setForeground(Color.BLUE);
                }
                if (selectedDate.getDayOfMonth() == cellDayValue) {//当前选择的天
                    label.setForeground(Colors.SELECTED_FONT);//选择天的前景色
                    label.setBackground(Colors.BG_CALENDAR_GRID_SELECTED);//选择天的背景色
                }
            }

            return label;
        }
    }


    public void addDateSelectionConstraint(DateSelectionConstraint constraint) {
        dateConstraints.add(constraint);
    }

    public void removeDateSelectionConstraint(DateSelectionConstraint constraint) {
        dateConstraints.remove(constraint);
    }

    public void removeAllDateSelectionConstraints() {
        dateConstraints.clear();
    }

    public Set<DateSelectionConstraint> getDateSelectionConstraints() {
        return Collections.unmodifiableSet(dateConstraints);
    }

    public boolean checkValid(DateModel<?> model) {
        for (DateSelectionConstraint constraint : dateConstraints) {
            if (!constraint.isValidSelection(model)) {
                return false;
            }
        }
        return true;
    }
}
